/*
    Very vulnerable ARM/ARM64code, for exploitation tutorials
    (c) B.Kerler 2018-2020
    MIT license
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <cstdint>
#if INTPTR_MAX == INT32_MAX
    #define d32BIT
#elif INTPTR_MAX == INT64_MAX
    #define d64BIT
#else
    #error "Environment not 32 nor 64-bit."
#endif

void ropgadgetstore() // Useful for rop practice
{
 #ifdef d32BIT
    __asm__("str r0, [r1]");
 #else
    __asm__("str x0, [x1]");
    __asm__("ldp x29, x30, [sp], #0x10");
    //__asm__("ret");
 #endif
}

void ropgadgetstack() // Useful for rop practice
{
 #ifdef d32BIT
    __asm__("pop {r0,r1,r2,r3,pc}");
 #else
    __asm__("ldp x0, x1, [sp]");
    __asm__("ldp x2, x3, [sp], #0x10");
    __asm__("ldp x29, x30, [sp], #0x20");
    //__asm__("ret");
 #endif
}


void jmpgadgetstack() // Useful for rop practice
{
 #ifdef d32BIT
    __asm__("pop {r4,r5,r6}");
    __asm__("pop {r1,r2,r3}");
    __asm__("blx r1");
 #else
    __asm__("ldp x8, x9, [sp],#0x28");
    __asm__("ldp x0, x1, [sp],#0x10");
    __asm__("ldp x2, x3, [sp],#0x10");
    __asm__("br x2");
    //__asm__("ret");
 #endif
}

char passwds[][14]=
{
        "hello",
        "help",
        "Velvet",
        "mysecret",
        "freedom",
        "happyness",
        "mypony",
        "Exploiter",
        "Gimme",
        "Fun",
        "Violet",
        "ropeme",
        "Magic",
        "Jumper"
};

bool comp(int flag)
{
    if (flag==0x5678)
    {
        printf("\nLevel 12 Password: \"%s\"\n",passwds[12]);
        exit(0);
    }
    return false;
}

/* LEVEL 14 */
void showflag(int flag, char* buffer)
{
    printf("Flag: %08x, You entered: %s\n",flag,buffer);
}


int jop(char * arg)
{
    void (*caller)(int,char*)={showflag};
    char buffer[32]={0};
    FILE *fr=fopen(arg,"r+b");
    int length=0;
    if (fr!=NULL){
        fread(&length,1,4,fr);
        fread(&buffer,1,length,fr);
    }
    else {
        printf("Token file not found.");
        exit(0);
    }
    int flag=0;
    caller(flag,buffer);
    if (flag==0x5678)
    {
        printf("\nLevel 14 passed. Well done !\n");
        exit(0);
    }
    else {
        printf("\nBad password given.\n");
    }
    return 0;
}


/* LEVEL 13 */
struct mapping
{
    char name[64];
    void (*function)();
    void (*destroy)();
};

char runcmd[512]={0};

void level13password()
{
    printf("\nWell done. Level 13 Password: Jumper\n");
    exit(0);
}

void run()
{
    if (runcmd==0)
    {
        printf("No valid command set up.\n");
        exit(0);
    }
    system(runcmd);
}

void destroy()
{
    printf("Mapping destroyed.\n");
}

struct mapping *mappingptr;

void destroymapping()
{
    if (mappingptr)
    {
        mappingptr->destroy(); //2. Then destroyed
    }
    free(mappingptr);
}

void new_mapping()
{
    char name[]="mymapping";
    mappingptr = (mapping*)malloc(512);  // 1. The size of 512 is important .... this is getting mapped
    strncpy(mappingptr->name,name,64);
    mappingptr->function=run;
    mappingptr->destroy=destroy;
    printf("Mapping created.\n");
}

void fillmapping(char* input)
{
    void* cmdbuffer=malloc(512); //3. New malloc will now point to the destroyed mappingptr and will be reused for cmdbuffer :D Size has to be equal or lower mappingptr malloc size
    memcpy(cmdbuffer,input,256);
    printf("Command buffer set as %s\n",(char*)cmdbuffer);
    memcpy(runcmd,cmdbuffer,512);
}

int use_after_free(char* options)
{
    printf("Please enter command:\n");
    char input[512]={"\0"};
    fgets(input,512,stdin);

    char* optptr=options;
    int flag=0;
    int i=0;
    int total=strlen(options);    
    for (i=total;i>0;i--)
    {
        flag=((int)optptr[total-i])-0x30;
        printf("\nFlag : %d\n",flag);
        if (flag==0)
        {
            printf("Creating new mapping.\n");
            new_mapping();
        }
        else if (flag==1)
        {
            destroymapping();
        }
        else if (flag==2)
        {
            if (mappingptr)
            {
                mappingptr->function();
            }
        }
        else if (flag==3)
        {
            fillmapping(input);
        }
        else
        {
            printf("\nInvalid flag.\n");
        }
    }
    return 0;   
}


/* LEVEL 12 */

void printme(char* arg, int flag)
{
    char buffer[60]={0};
    memcpy(buffer,arg,strlen(arg)*2); //something wrong over here, hmm ? Bad typo :D
    printf("Flag: %08x, You entered: %s\n",flag,buffer);
}

int rop(char * arg)
{
    int flag=0x1234;    
    printme(arg,flag);
    if (!comp(flag))  printf("\nBad password given.\n");
    return 0;
}


/* LEVEL 11 */
int path_traversal(char *path)
{
    char *buffer = (char*)malloc(sizeof(char)*(strlen("ls ") + strlen(path)));
    strcpy(buffer, "ls ");
    strcat(buffer, path);
    setresuid(geteuid(), geteuid(), geteuid());
    if (!(strncmp(path,"dir1/dir2",strlen("dir1/dir2"))==0) && !(strncmp(path,"./dir1/dir2",strlen("./dir1/dir2"))==0))
    {
      printf("Only directory ./dir1/dir2/ and subdirectories may be listed!\n");
      exit(0);
    }

    system(buffer); //Let's run something other than man, shall we ?
    
        if (!strncmp(path,"dir1/dir2/../..",strlen("dir1/dir2/../..")) || !strncmp(path,"./dir1/dir2/../..",strlen("./dir1/dir2/../..")))
    {
      printf("\nLevel 12 Password: \"%s\"\n",passwds[11]);
    }
    free(buffer);
    return 0;
}

/* LEVEL 10 */
int cmd_inject(int argc, char *argv[])
{
    char *buffer = (char*)malloc(sizeof(char)*(strlen("man ") + strlen(argv[2])));
    strcpy(buffer, "man ");
    strcat(buffer, argv[2]);
    setresuid(geteuid(), geteuid(), geteuid());
    system(buffer); //Let's run something other than man, shall we ?
        if (strchr(buffer,';')!=NULL)
    {
      printf("\nLevel 11 Password: \"%s\"\n",passwds[10]);
    }
    free(buffer);
    return 0;
}

/* LEVEL 9 */
int nullify(unsigned long addr, int option)
{
    int important=1;
    int *ptr = &important;
    *ptr=2;    
    
    unsigned int *addrptr=(unsigned int*)addr;
    if (option==1)
    {
        *addrptr=0;
    }
    printf("Address: %08lX, Important ptr: %08lX, Important value: %d\n",addr,&important,important);

    if (important==0)
    {
        printf("Level 10 Password: \"%s\"\n",passwds[9]);
    }
    return 0;
}


/* LEVEL 8 */
class Root {};

class Msg : public Root
{
    public:
        virtual void msg(const char *strng)
        {
            printf("Level 9 Password: \"%s\", welcome %s\n",passwds[8],strng);
        }
};

class Run : public Root
{
    public:
        virtual void run(const char *cmd)
        {
            system(cmd);
        }
};

int type_confusion(char* arg)
{
    Root *b = new Msg();
    Root *a = new Run();
    Run *g;
    g = static_cast<Run*>(a); //What happens if a is b instead ? :D
    printf("Current g ptr, addr: %p\n",g); 
    printf("Current b ptr, addr: %p,%lx\n",b,&b);    
    char tmp[64] = {'\0'};
    int flag=0;
    strcpy(tmp,arg);
    g->run(tmp);
    delete(a);
    delete(b);
    return 0;
}

/* LEVEL 7 */
struct Heap1
{
    char data[32];
};

struct Heap2
{
    int magic;
};

int heap_overflow(char* text)
{

    struct Heap1* heap1 = new Heap1; //alternatively could use malloc
    struct Heap2* heap2 = new Heap2;

    heap2->magic = 0;
    strcpy(heap1->data, text);

    printf("Heap magic number: 0x%x\n", heap2->magic);

    if (heap2->magic == 0x6763)
    {
        printf("Level 8 Password: \"%s\"\n",passwds[7]);
    }

    return 0;
}


/* LEVEL 6 */
char Password[100]; // Memory storage for the password

char goodPassword() {
  int good='N';
  int *p = &good;
  fgets(Password, sizeof(Password), stdin); // Get input from keyboard

  printf("Password=");
  printf(Password);
  printf("\n");

  return (char)(*p);
}

int format_string(char * input)
{
  printf("Enter your password:");

  char r = goodPassword();
  printf("r=%c\n", r);

  if (r == 'Y') {
    printf("Level 7 Password: \"%s\"\n",passwds[6]);
  }
  else {
    printf("No Level Password for you today.\n");
    exit(-1);
  }

  return 0;
}

/* LEVEL 5 */
unsigned int secret=0x1337;

void level3password()
{
    printf("Level 3 Password: \"%s\"\n",passwds[2]);
    return;
}
int stack_cookie(char * arg)
{
    unsigned int stack_cookie = secret;
    char flag[1] ={0};  
    char tmp[64] = {'\0'};
    
    strcpy(tmp,arg);

    if (stack_cookie != secret)
    {
        printf("Error: Stack corrupted !");
        exit(1);
    }
    
    printf("Running normally\n");
    if (flag[0]==1)    // Change the flag through buffer overflow, but mind the stack cookie !
    {
        printf("Level 6 Password: \"%s\"\n",passwds[5]);
    }
    return 1;
}


/* LEVEL 4 */
void level5password()
{
    printf("Level 5 Password: \"%s\"\n",passwds[4]);
}

int off_by_one(char * arg)
{
    #pragma pack(push,1)
    char flag[1]={1};   
    char buffer[256];
    #pragma pack(pop)
    if (strlen(arg)>256)
    {
        printf("Length higher 256 not allowed !\n");
        exit(1);
    }
    strcpy(buffer,arg);
    if (flag[0]==0)
    {
        level5password();
    }
    return 0;
}

/* LEVEL 3 */
void level4password()
{
    printf("Level 4 Password: \"%s\"\n",passwds[3]);
    return;
}


#ifdef d32BIT
void array_overflow(int position, int value)
{
    int array[32];
    array[position]=value;
    printf("filling array position %d with %d\n", position, value);
    return;
}
#else
void copy_array(int position, int value)
{
    int array[32];
    array[position]=value;
    printf("filling array position %d with %d\n", position, value);
}
void array_overflow(int position, int value)
{
    copy_array(position,value);
    return;
}
#endif

/* LEVEL 2 */


#ifdef d32BIT
int stack_overflow(char* username, char* password)
{
    char buffer[8] = {"\0"};
    char admin_user[]="admin";
    char admin_pass[]="funny";
    
    int i;
    strcpy(buffer, username);
    buffer[strlen(admin_user)]='\0';
    if (strcmp(buffer, admin_user))
    {
        printf("Login failed\n");
        return 0;
    }

    strcpy(buffer, password);
    buffer[strlen(admin_pass)]='\0';
    if (strcmp(buffer, admin_pass))
    {
        printf("Login failed\n");
        return 0;
    }
    printf("Login succeeded, but still you failed :P\n");
    return 1;
}
#else
bool verify_user(char* username)
{
    char buffer[8] = {"\0"};
    char admin_user[]="admin";
    strcpy(buffer, username);
    buffer[strlen(admin_user)]='\0';
    if (strcmp(buffer, admin_user))
    {
        printf("Login failed\n");
        return false;
    }
    return true;
}

int stack_overflow(char* username, char* password)
{
    char buffer[8] = {"\0"};
    if (!verify_user(username)) return 0;
    char admin_pass[]="funny";    
    strcpy(buffer, password);
    buffer[strlen(admin_pass)]='\0';
    if (strcmp(buffer, admin_pass))
    {
        printf("Login failed\n");
        return 0;
    }
    printf("Login succeeded, but still you failed :P\n");
    return 1;
}
#endif


/* LEVEL 1 */
int int_overflow(char* value)
{
    unsigned short number;
    int i = atoi(value);
    unsigned int h=i;
    if (h<=0)
    {
        printf("Value less or equal 0 is not allowed.\n");
        exit(0);
    }
    number=i;
    
    if (number!=0)
    {
        printf("Value %d defined.\n",number);
        exit(0);
    }

    if (i<0 || number==0) // Two ways of overflow possible ... int AND short :)
    {
        printf("Level 2 Password: \"%s\"\n",passwds[1]);
    }
return 0;
}


int main(int argc, char *argv[])
{
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);

    if (argc < 2)
    {
        printf("usage: %s <level_password> <arg1> <arg2>\n", argv[0]);
            printf("Level 1 Password=\"hello\"\n");
        exit(0);
    }
    
    int level=0;
    if (argc>=2)
    {
        if (!strcmp(argv[1],passwds[0]))
        {
            if (argc < 3)
            {
                 printf("usage: %s hello <value>\n", argv[0]);
                 exit(0);
            }
            setresuid(geteuid(),geteuid(),geteuid());
            int_overflow(argv[2]);
            exit(0);
        }
        else
        if (!strcmp(argv[1],passwds[1]))
        {
            if ((argc < 4))
            {
                 printf("usage: %s %s <username> <password>\n", argv[0],passwds[1]);
                 exit(0);
            }
            setresuid(geteuid(),geteuid(),geteuid());
            stack_overflow(argv[2],argv[3]);
            exit(0);
        }
        else
        if (!strcmp(argv[1],passwds[2]))
        {
            if ((argc < 4))
            {
                 printf("usage: %s %s <arraypos> <value>\n", argv[0],passwds[2]);
                 exit(0);
            }
            setresuid(geteuid(),geteuid(),geteuid());
            array_overflow(atoi(argv[2]), atoi(argv[3]));
            exit(0);
        }
        else
        if (!strcmp(argv[1],passwds[3]))
        {
            if ((argc < 3))
            {
                 printf("usage: %s %s <magic>\n", argv[0],passwds[3]);
                 exit(0);
            }
            setresuid(geteuid(),geteuid(),geteuid());
            off_by_one(argv[2]);
            exit(0);
        }
        else
        if (!strcmp(argv[1],passwds[4]))
        {
            if ((argc < 3))
            {
                 printf("usage: %s %s <magic>\n", argv[0],passwds[4]);
                 exit(0);
            }
            setresuid(geteuid(),geteuid(),geteuid());
            stack_cookie(argv[2]);
            exit(0);
        }
        else
        if (!strcmp(argv[1],passwds[5]))
        {
            if ((argc < 2))
            {
                 printf("usage: %s %s\n", argv[0],passwds[5]);
                 exit(0);
            }
            setresuid(geteuid(),geteuid(),geteuid());
            //char input[255]={"\0"};
            //scanf("%s",input); 
            format_string(argv[2]);
            exit(0);
        }
        else
        if (!strcmp(argv[1],passwds[6]))
        {
            if ((argc < 3))
            {
                 printf("usage: %s %s <text>\n", argv[0],passwds[6]);
                 exit(0);
            }
            setresuid(geteuid(),geteuid(),geteuid());
            heap_overflow(argv[2]);
            exit(0);
        }
        else
        if (!strcmp(argv[1],passwds[7]))
        {
            if ((argc < 3))
            {
                 printf("usage: %s %s <cmd>\n", argv[0],passwds[7]);
                 exit(0);
            }
            setresuid(geteuid(),geteuid(),geteuid());
            type_confusion(argv[2]);
            exit(0);
        }
        else
        if (!strcmp(argv[1],passwds[8]))
        {
            if ((argc < 4))
            {
                 printf("usage: %s %s <addr> <flag>\n", argv[0],passwds[8]);
                 exit(0);
            }
            setresuid(geteuid(),geteuid(),geteuid());
            nullify(strtoul(argv[2],0,16),atoi(argv[3]));
            exit(0);
        }
    else
        if (!strcmp(argv[1],passwds[9]))
        {
                if ((argc < 3) || (strlen(argv[2]) > 42 ))
                {
                     printf("usage: %s %s <program>\n", argv[0],passwds[9]);
                     exit(0);
                }
                setresuid(geteuid(),geteuid(),geteuid());
                cmd_inject(argc,argv);
                exit(0);
        }
    else    
    if (!strcmp(argv[1],passwds[10]))
        {
                if  (argc < 3)
                {
                printf("usage: %s %s <path>\n", argv[0],passwds[10]);
                exit(0);
                }
                setresuid(geteuid(),geteuid(),geteuid());
                path_traversal(argv[2]);
                exit(0);
        }
    else
    if (!strcmp(argv[1],passwds[11]))
        {
                if  (argc < 2)
                {
                printf("usage: %s %s <arg>\n", argv[0],passwds[11]);
                exit(0);
                }
                setresuid(geteuid(),geteuid(),geteuid());
                printf("Please enter your magic stuff:\n");
                char input[255]={"\0"};
                scanf("%s",input);
                rop(input);
                exit(0);
        }
    else
    if (!strcmp(argv[1],passwds[12]))
        {
                if  (argc < 3)
                {
                    printf("usage: %s %s <options> <cmd>\n", argv[0],passwds[12]);
                    printf("loopable options are \"0\"=new mapped device, \"1\"=destroy mapped device, \"2\"=run device command,  \"3\"=setup device command\n"); 
                    exit(0);
                }
                setresuid(geteuid(),geteuid(),geteuid());
                use_after_free(argv[2]);
                exit(0);
        }
    else
    if (!strcmp(argv[1],passwds[13]))
    {
        if ((argc < 3))
            {
                 printf("usage: %s %s <filename>\n", argv[0],passwds[13]);
                 exit(0);
            }
            setresuid(geteuid(),geteuid(),geteuid());
            jop(argv[2]);
            exit(0);
    }
    else {
        printf("Unknown level password given or invalid options.\n");
    }
}
    exit(0);
    ropgadgetstore();
    ropgadgetstack();
    jmpgadgetstack();
}


